import axios from 'axios'
import {compare as semverCompare} from 'semver'
import {Observable, from, forkJoin, map, of} from 'rxjs'
import {Injectable, Inject} from '@angular/core'
import {Logger, LogService, PlatformService, BOOTSTRAP_DATA, BootstrapData, PluginInfo} from 'tabby-core'
import {PLUGIN_BLACKLIST} from '../../../app/src/pluginBlacklist'

const OFFICIAL_NPM_ACCOUNT = 'eugenepankov'


// 装饰器，用于标记该服务在根模块中提供
@Injectable({providedIn: 'root'})
export class PluginManagerService {
    logger: Logger // 日志记录器
    userPluginsPath: string // 用户插件路径
    installedPlugins: PluginInfo[] // 已安装的插件列表

    // 私有构造函数，依赖注入所需的服务
    private constructor(
        log: LogService, // 日志服务
        private platform: PlatformService, // 平台服务
        @Inject(BOOTSTRAP_DATA) bootstrapData: BootstrapData, // 引导数据
    ) {
        this.logger = log.create('pluginManager') // 创建一个命名为'pluginManager'的日志记录器
        this.installedPlugins = [...bootstrapData.installedPlugins] // 初始化已安装插件列表
        this.installedPlugins.sort((a, b) => a.name.localeCompare(b.name)) // 按名称排序已安装插件
        this.userPluginsPath = bootstrapData.userPluginsPath // 初始化用户插件路径
    }

    // 列出可用的插件，支持查询
    listAvailable(query?: string): Observable<PluginInfo[]> {
        return forkJoin(
            this._listAvailableInternal('tabby-', 'tabby-plugin', query), // 获取tabby插件
            this._listAvailableInternal('terminus-', 'terminus-plugin', query), // 获取terminus插件
        ).pipe(
            // 将结果合并成一个数组
            map(x => x.reduce((a, b) => a.concat(b), [])),
            // 去重插件
            map(x => {
                const names = new Set<string>()
                return x.filter(item => {
                    if (names.has(item.name)) {
                        return false
                    }
                    names.add(item.name)
                    return true
                })
            }),
            // 按名称排序插件
            map(x => x.sort((a, b) => a.name.localeCompare(b.name))),
        )
    }

    // 列出已安装的插件，支持查询
    listInstalled(query: string): Observable<PluginInfo[]> {
        return of(this.installedPlugins.filter(x => x.name.includes(query))) // 根据查询过滤已安装插件
    }

    // 内部方法，列出可用的插件
    _listAvailableInternal(namePrefix: string, keyword: string, query?: string): Observable<PluginInfo[]> {
        return from(
            axios.get(`https://registry.npmjs.com/-/v1/search?text=keywords%3A${keyword}%20${query}&size=250`), // 向npm注册表发起搜索请求
        ).pipe(
            // 处理响应数据
            map(response => response.data.objects
                .filter(item => !item.keywords?.includes('tabby-dummy-transition-plugin')) // 过滤掉无效的插件
                .map(item => ({
                    name: item.package.name.substring(namePrefix.length), // 提取插件名称
                    packageName: item.package.name, // 包名称
                    description: item.package.description, // 插件描述
                    version: item.package.version, // 插件版本
                    homepage: item.package.links.homepage, // 插件主页
                    author: item.package.author?.name, // 插件作者
                    isOfficial: item.package.publisher.username === OFFICIAL_NPM_ACCOUNT, // 是否官方插件
                })),
            ),
            // 过滤插件名称前缀不匹配的插件
            map(plugins => plugins.filter(x => x.packageName.startsWith(namePrefix))),
            // 过滤黑名单中的插件
            map(plugins => plugins.filter(x => !PLUGIN_BLACKLIST.includes(x.packageName))),
            // 按插件名称分组并取最新版本
            map(plugins => {
                const mapping: Record<string, PluginInfo[]> = {}
                for (const p of plugins) {
                    mapping[p.name] ??= [] // 如果分组不存在，则初始化
                    mapping[p.name].push(p) // 插件加入分组
                }
                return Object.values(mapping).map(list => {
                    list.sort((a, b) => -semverCompare(a.version, b.version)) // 按版本排序
                    return list[0] // 取最新版本
                })
            }),
            // 按名称排序插件
            map(plugins => plugins.sort((a, b) => a.name.localeCompare(b.name))),
        )
    }

    // 安装插件
    async installPlugin(plugin: PluginInfo): Promise<void> {
        try {
            // 调用平台服务安装插件
            await this.platform.installPlugin(plugin.packageName, plugin.version)
            // 过滤掉已安装插件列表中的同名插件（旧版本）
            this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
            // 将新安装的插件添加到已安装插件列表
            this.installedPlugins.push(plugin)
        } catch (err) {
            // 如果安装过程中出错，记录错误日志
            this.logger.error(err)
            // 抛出错误，以便上层调用者处理
            throw err
        }
    }

    // 卸载插件
    async uninstallPlugin(plugin: PluginInfo): Promise<void> {
        try {
            // 调用平台服务卸载插件
            await this.platform.uninstallPlugin(plugin.packageName)
            // 从已安装插件列表中过滤掉被卸载的插件
            this.installedPlugins = this.installedPlugins.filter(x => x.packageName !== plugin.packageName)
        } catch (err) {
            // 如果卸载过程中出错，记录错误日志
            this.logger.error(err)
            // 抛出错误，以便上层调用者处理
            throw err
        }
    }
}
